﻿using AutoMapper;
using System;
using System.Collections;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace BunnyEater.Common.Extensions
{
    /// <summary>
    /// 封装全局类型映射静态类方法--使用AutoMapper扩展对象
    /// </summary>
    public static class AutoMapperExtension
    {
        private static readonly ConcurrentDictionary<(Type, Type), MapperConfiguration> _configCache = new();
        private static readonly ConcurrentDictionary<(Type, Type), IMapper> _mapperCache = new();
        private static Action<IMappingExpression> _mappingConfigurationAction;

        /// <summary>
        /// 创建Mapper缓存实列提高性能
        /// </summary>
        /// <typeparam name="TSource">源类型</typeparam>
        /// <typeparam name="TDestination">目标类型</typeparam>
        /// <returns></returns>
        private static IMapper GetMapper<TSource, TDestination>()
        {
            var key = (typeof(TSource), typeof(TDestination));
            return _mapperCache.GetOrAdd(key, _ =>
            {
                var config = new MapperConfiguration(cfg => cfg.CreateMap<TSource, TDestination>());
                return config.CreateMapper();
            });
        }

        /// <summary>
        /// 重载Mapper缓存实例方法提高适用性
        /// </summary>
        /// <typeparam name="TDestination">目标类型</typeparam>
        /// <param name="sourceType">待映射的源目标类型</param>
        /// <returns></returns>
        private static IMapper GetMapper<TDestination>(Type sourceType)
        {
            var cacheKey = (sourceType, typeof(TDestination));
            var config = _configCache.GetOrAdd(cacheKey, key =>
            {
                var newConfig = new MapperConfiguration(cfg =>
                {
                    var expression = cfg.CreateMap(key.Item1, key.Item2);
                    _mappingConfigurationAction?.Invoke(expression);
                });
                return newConfig;
            });
            return _mapperCache.GetOrAdd(cacheKey, key => config.CreateMapper());
        }

        /// <summary>
        /// 实体类对象映射
        /// </summary>
        /// <typeparam name="T">源类型</typeparam>
        /// <param name="obj">待映射的对象</param>
        /// <returns></returns>
        public static T Mapto<T>(this object obj)
        {
            if (obj == null) return default;
            var sourceType = obj.GetType();
            /***
            //var cacheKey = (sourceType, typeof(T));

            //var config = _configCache.GetOrAdd(cacheKey, key =>
            //{
            //    var newConfig = new MapperConfiguration(cfg =>
            //    {
            //        var expression = cfg.CreateMap(key.Item1, key.Item2);
            //        _mappingConfigurationAction?.Invoke(expression);
            //    });
            //    return newConfig;
            //});

            //var mapper = _mapperCache.GetOrAdd(cacheKey, key => config.CreateMapper());
            ***/
            var mapper = GetMapper<T>(sourceType);
            return mapper.Map<T>(obj);
        }

        /// <summary>
        /// 实体类集合映射
        /// </summary>
        /// <typeparam name="T">目标类型</typeparam>
        /// <param name="objects">待映射的对象</param>
        /// <returns></returns>
        public static List<T> MaptoList<T>(this IEnumerable objects)
        {
            if (objects == null) return default;
            var souceType = objects.GetType().GetGenericArguments()[0];
            /***
            //var cacheKey = (souceType, typeof(T));

            //var config = _configCache.GetOrAdd(cacheKey, key =>
            //{
            //    var newConfig = new MapperConfiguration(cfg =>
            //    {
            //        var expression = cfg.CreateMap(key.Item1, key.Item2);
            //        _mappingConfigurationAction?.Invoke(expression);
            //    });
            //    return newConfig;
            //});

            //var mapper = _mapperCache.GetOrAdd(cacheKey, key => config.CreateMapper());
            ***/
            var mapper = GetMapper<T>(souceType);
            return mapper.Map<List<T>>(objects);
        }

        /// <summary>
        /// 集合列表类型映射
        /// </summary>
        /// <typeparam name="TSource">源类型</typeparam>
        /// <typeparam name="TDestination">目标类型</typeparam>
        /// <param name="source">待映射的源对象</param>
        /// <returns></returns>
        public static List<TDestination> MapToList<TSource, TDestination>(this IEnumerable source)
        {
            if (source == null) return new List<TDestination>();
            var mapper = GetMapper<TSource, TDestination>();
            return mapper.Map<List<TDestination>>(source);
        }

        /// <summary>
        /// 源对象映射到目标对象
        /// </summary>
        /// <typeparam name="TSource">源类型</typeparam>
        /// <typeparam name="TDestination">目标类型</typeparam>
        /// <param name="source">待映射的源对象实例</param>
        /// <param name="destination">可选的目标对象实例</param>
        /// <returns></returns>
        public static TDestination Mapto<TSource, TDestination>(this TSource source, TDestination destination)
        {
            if (source == null) return destination;
            var mapper = GetMapper<TSource, TDestination>();
            return mapper.Map(source, destination);
        }

        /// <summary>
        ///  类型映射
        /// </summary>
        public static T MapTo<T>(this object obj)
        {
            if (obj == null) return default(T);

            var config = new MapperConfiguration(m => m.CreateMap(obj.GetType(), typeof(T)));
            var mapper = config.CreateMapper();
            return mapper.Map<T>(obj);
        }
    }
}
